<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>环境光源</title>
    <style type="text/css">
      html,
      body {
        margin: 0;
        height: 100%;
      }

      canvas {
        display: block;
      }
      .myexplain {
        position: absolute;
        top: 10px;
        left: 100px;
        z-index: 99;
        background: #fff;
        margin: 0;
        padding: 0;
        display: none;
      }
      .btn {
        position: absolute;
        top: 1px;
        left: 5px;
        z-index: 100;
      }
    </style>
  </head>
  <body onload="draw();">
    <pre class="myexplain" id="myexplain">
        名字	                  描述
        THREE.AmbientLight	   这是一个基础光源，也叫环境光源，该光源的颜色将会叠加到场景现有的颜色上面，无法创建阴影。
        THREE.PointLight	   点光源，从空间的一点向所有方向发射光源。
        THREE.SpotLight	         这种光源有聚光的效果，类似台灯、天花板上的吊灯或者手电筒。
        THREE.DirectionalLight	 无限光，平行光。从这种光源发出的光线可以看作是平行的，比如太阳光。
        THREE.HemisphereLight	 这是一种特殊光源，可以通过模拟反光面和光线微弱的天空来创建更加自然的室外光线。无法创建阴影。
        THREE.AreaLight	         使用这种光源可以指定散发光线的平面，而不是一个点。无法创建阴影。
        THREE.LensFlare	         这不是一种光源，但是通过使用THREE.LensFlare，可以为场景中的光源添加镜头光晕效果。

        THREE.Color对象方法
        函数名	                 描述
        set(value)	将当前颜色设置为指定的十六进制值。这个值可以是字符串、数值或是已有的THREE.Color实例。
        setHex(value)	将当前颜色设置为指定的十六进制值。
        setRGB(r,g,b)	根据提供的RGB值设置颜色，参数范围从0到1
        setHSL(h,s,l)	根据提供的HSL值设置颜色。参数范围从0到1
        setStyle(style)	根据css设置颜色的方式来设置颜色。例如使用”rgb(255,0,0)”、”#fff”、”red”
        copy(color)	从提供的颜色对象复制颜色值到当前对象
        copyGammaToLinear(color)	通常在内部使用，用THREE.Color提供的示例设置对象的颜色。颜色由伽马色彩空间转换到线性色彩空间得来的。伽马色彩空间也使用RGB颜色，但是会使用指数系数而不是线性系数
        copyLinearToGamma(color)	通常在内部使用。用THREE.Color提供的示例设置对象的颜色，颜色通常是有线性色彩空间转换到伽马色彩空间得来的
        convertGammaToLinear()	将当前颜色从伽马色彩空间转换到线性色彩空间
        convertLinearToGamma()	将当前颜色从线性色彩空间转换到干嘛色彩空间
        getHex()	以十六进制值形式从颜色对象中获取颜色值
        getHexString()	以十六进制字符串形式从颜色对象中获取颜色值
        getStyle()	以css值得形式从颜色对象中获取颜色值
        getHSL(optionalTarget)	以HSL值得形式从颜色对象中获取颜色值。如果提供了optionTarget对象，Three.js将吧h、s和l属性设置到该对象
        offsetHSL(h,s,l)	将提供的h、s和l值添加到当前颜色的h、s和l值上
        add(color)	将r、g和b值添加到当前颜色
        addColors(color1,color2)	通常在内部使用，将color1和color2相加，再将得到的值设置到当前颜色
        addScalar(s)	通常在内部使用，在当前颜色RGB分量上添加值，谨记参数范围从0到1
        multiply(color)	通常在内部使用，将当前颜色的RGB值与THREE.Color对象的RGB值相乘
        multiplyScalar(s)	通常在内部使用，将当前颜色RGB值与提供的RGB值相乘，谨记参数范围从0到1
        lerp(color,alpha)	通常在内部使用，找出介于对象的颜色和提供的颜色之间的颜色，alpha属性定义了当前颜色与提供的颜色的差距
        equals(color)	如果THREE.Color对象实例提供的颜色的RGB值与当前颜色相等，则返回true
        formArray(array)	与setRGB方法具有相同的功能，只是RGB值是通过数字数组的方式作为参数传入
        toArray()	返回三个元素的数组：[r,g,b]
        clone()	复制当前颜色
    </pre>
    <button onclick="preswitch()" class="btn">查看文档</button>
  </body>
  <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r128/three.js"></script>
  <script src="./js/controls/OrbitControls.js"></script>
  <script src="./js/libs/dat.gui.min.js"></script>
  <script>
    function preswitch() {
      var pre = document.getElementById("myexplain");
      if (pre.style.display == "block") {
        pre.style.display = "none";
      } else {
        pre.style.display = "block";
      }
      // document.getElementById('myexplain').style.display='block'?'none':document.getElementById('myexplain').style.display='none'?'block':''
    }
    var renderer;
    function initRender() {
      renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(window.innerWidth, window.innerHeight);
      //告诉渲染器需要阴影效果
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是，没有设置的这个清晰 THREE.PCFShadowMap
      document.body.appendChild(renderer.domElement);
    }

    var camera;
    function initCamera() {
      camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        1,
        10000
      );
      camera.position.set(0, 0, 100);
    }

    var scene;
    function initScene() {
      scene = new THREE.Scene();
    }
    //初始化dat.GUI简化试验流程
    var gui;
    function initGui() {
      //声明一个保存需求修改的相关数据的对象
      gui = {
        ambientLight: "#111111", //环境光源
        pointLight: "#ffffff", //点光源
        lightY: 30, //灯光y轴的位置
        boxgX: 5, //立方体的x轴位置
        boxgY: 5, //立方体的x轴位置
        boxgZ: -5, //立方体的z轴的位置
      };
      var datGui = new dat.GUI();
      //将设置属性添加到gui当中，gui.add(对象，属性，最小值，最大值）
      datGui.addColor(gui, "ambientLight").onChange(function (e) {
        ambientLight.color = new THREE.Color(e);
      });
      datGui.addColor(gui, "pointLight").onChange(function (e) {
        pointLight.color = new THREE.Color(e);
      });
      datGui.add(gui, "lightY", 0, 100);
      datGui.add(gui, "boxgX", -30, 30);
      datGui.add(gui, "boxgY", -30, 30);
      datGui.add(gui, "boxgZ", -30, 30);
    }

    var ambientLight, pointLight;
    function initLight() {
      ambientLight = new THREE.AmbientLight("#111111");
      scene.add(ambientLight);

      pointLight = new THREE.PointLight("#ffffff");
      pointLight.position.set(15, 30, 10);

      //告诉平行光需要开启阴影投射
      pointLight.castShadow = true;

      scene.add(pointLight);
    }
    var boxg;
    function initModel() {
      //立方体
      var BoxGeometry = new THREE.BoxGeometry(30, 30, 30);
      var boxgMaterial = new THREE.MeshLambertMaterial({ color: 0x00ffff });

      boxg = new THREE.Mesh(BoxGeometry, boxgMaterial);

      //告诉立方体需要投射阴影
      boxg.castShadow = true;
      scene.add(boxg);
      //底部平面
      var planeGeometry = new THREE.PlaneGeometry(200, 200);
      var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xaaaaaa });

      var plane = new THREE.Mesh(planeGeometry, planeMaterial);
      plane.rotation.x = -0.5 * Math.PI;
      plane.position.y = -15;

      //告诉底部平面需要接收阴影
      plane.receiveShadow = true;

      scene.add(plane);
    }

    //用户交互插件 鼠标左键按住旋转，右键按住平移，滚轮缩放
    var controls;
    function initControls() {
      controls = new THREE.OrbitControls(camera, renderer.domElement);

      // 如果使用animate方法时，将此函数删除
      //controls.addEventListener( 'change', render );
      // 使动画循环使用时阻尼或自转 意思是否有惯性
      controls.enableDamping = true;
      //动态阻尼系数 就是鼠标拖拽旋转灵敏度
      controls.dampingFactor = 0.25;
      //是否可以缩放
      controls.enableZoom = true;
      //是否自动旋转
      controls.autoRotate = true;
      //设置相机距离原点的最近距离
      controls.minDistance = 200;
      //设置相机距离原点的最远距离
      controls.maxDistance = 600;
      //是否开启右键拖拽
      controls.enablePan = true;
    }

    function render() {
      renderer.render(scene, camera);
    }

    //窗口变动触发的函数
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      render();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function animate() {
      //更新控制器
      controls.update();
      render();
      //更新相关位置
      pointLight.position.y = gui.lightY;
      boxg.position.x = gui.boxgX;
      boxg.position.y = gui.boxgY;
      boxg.position.z = gui.boxgZ;
      requestAnimationFrame(animate);
    }

    function draw() {
      initRender();
      initScene();
      initCamera();
      initLight();
      initModel();
      initControls();
      initGui();
      animate();
      window.onresize = onWindowResize;
    }
  </script>
</html>
